/*
* Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*/

#include <QNdefMessage>

#include "nearfielditem.h"
#include "sharedmemory.h"

NearFieldItem::NearFieldItem(QObject *parent) :
    QObject(parent),
    m_sharedMemory(new SharedMemory(this)),
    m_nearFieldManager(new QNearFieldManager(this)),
    m_targetAccessMode(NoTargetAccess),
    m_ndefRecordType(),
    m_ndefRecordPayload(),
    m_errorCategory(NoErrorCategory),
    m_error(NoError)
{
    connect(m_sharedMemory, SIGNAL(error(int)), SLOT(handleSharedMemoryError(int)));

    if (m_nearFieldManager->isAvailable()) {
        connect(m_nearFieldManager, SIGNAL(targetDetected(QNearFieldTarget *)), SLOT(handleTargetDetected(QNearFieldTarget *)));
        connect(m_nearFieldManager, SIGNAL(targetLost(QNearFieldTarget *)), SLOT(handleTargetLost(QNearFieldTarget *)));
    } else {
        setError(NearFieldManagerErrorCategory, NearFieldManagerNotAvailableError);
    }
}

NearFieldItem::~NearFieldItem()
{
    // Empty implementation.
}

NearFieldItem::TargetAccessMode NearFieldItem::targetAccessMode() const
{
    return m_targetAccessMode;
}

QString NearFieldItem::ndefRecordType() const
{
    return m_ndefRecordType;
}

QString NearFieldItem::ndefRecordPayload() const
{
    return m_ndefRecordPayload;
}

NearFieldItem::ErrorCategory NearFieldItem::errorCategory() const
{
    return m_errorCategory;
}

int NearFieldItem::error() const
{
    return m_error;
}

void NearFieldItem::setTargetAccessMode(TargetAccessMode targetAccessMode)
{
    if (m_targetAccessMode != targetAccessMode) {
        const bool targetDetectionStarted = m_targetAccessMode != NoTargetAccess;
        bool targetAccessModeChangeable = true;

        if (targetDetectionStarted && (targetAccessMode == NoTargetAccess)) {
            m_nearFieldManager->stopTargetDetection();
        } else if (!targetDetectionStarted && (targetAccessMode != NoTargetAccess)) {
            targetAccessModeChangeable = m_nearFieldManager->startTargetDetection();
        }

        if (targetAccessModeChangeable) {
            m_nearFieldManager->setTargetAccessModes(static_cast<QNearFieldManager::TargetAccessMode>(targetAccessMode));
            m_targetAccessMode = targetAccessMode;

            emit targetAccessModeChanged(m_targetAccessMode);
        } else {
            setError(NearFieldManagerErrorCategory, NearFieldManagerUnableToStartTargetDetectionError);
        }
    }
}

void NearFieldItem::setNdefRecordType(const QString &ndefRecordType)
{
    if (m_ndefRecordType != ndefRecordType) {
        m_ndefRecordType = ndefRecordType;
        emit ndefRecordTypeChanged(m_ndefRecordType);
    }
}

void NearFieldItem::setNdefRecordPayload(const QString &ndefRecordPayload)
{
    if (m_ndefRecordPayload != ndefRecordPayload) {
        m_ndefRecordPayload = ndefRecordPayload;
        emit ndefRecordPayloadChanged(m_ndefRecordPayload);
    }
}

void NearFieldItem::readSharedMemory()
{
    if (m_sharedMemory->isModified()) {
        const QNdefMessage ndefMessage(QNdefMessage::fromByteArray(m_sharedMemory->message()));

        handleNdefMessageRead(ndefMessage);
    }
}

void NearFieldItem::handleSharedMemoryError(int error)
{
    setError(SharedMemoryErrorCategory, error);
}

void NearFieldItem::handleTargetDetected(QNearFieldTarget *target)
{
    switch (m_targetAccessMode) {
    case NdefReadTargetAccess:
        readNdefMessages(target);
        break;
    case NdefWriteTargetAccess:
        writeNdefMessage(target);
        break;
    default:
        break;
    }

    emit targetDetected();
}

void NearFieldItem::handleTargetLost(QNearFieldTarget *target)
{
    target->deleteLater();

    emit targetLost();
}

void NearFieldItem::handleTargetError(QNearFieldTarget::Error error, const QNearFieldTarget::RequestId &id)
{
    Q_UNUSED(id);

    setError(NearFieldTargetErrorCategory, error);
}

void NearFieldItem::handleNdefMessageRead(const QNdefMessage &message)
{
    foreach (const QNdefRecord &record, message) {
        if (record.type() == m_ndefRecordType) {
            setNdefRecordType(record.type());
            setNdefRecordPayload(record.payload());
            break;
        }
    }

    emit ndefMessageRead();
}

void NearFieldItem::handleNdefMessagesWritten()
{
    emit ndefMessageWritten();
}

void NearFieldItem::readNdefMessages(QNearFieldTarget *target)
{
    connect(target, SIGNAL(ndefMessageRead(const QNdefMessage &)), SLOT(handleNdefMessageRead(const QNdefMessage &)));
    connect(target, SIGNAL(error(QNearFieldTarget::Error, const QNearFieldTarget::RequestId &)), SLOT(handleTargetError(QNearFieldTarget::Error, const QNearFieldTarget::RequestId &)));
    target->readNdefMessages();
}

void NearFieldItem::writeNdefMessage(QNearFieldTarget *target)
{
    connect(target, SIGNAL(ndefMessagesWritten()), SLOT(handleNdefMessagesWritten()));
    connect(target, SIGNAL(error(QNearFieldTarget::Error, const QNearFieldTarget::RequestId &)), SLOT(handleTargetError(QNearFieldTarget::Error, const QNearFieldTarget::RequestId &)));

    QNdefRecord ndefRecord;
    ndefRecord.setTypeNameFormat(QNdefRecord::ExternalRtd);
    ndefRecord.setType(m_ndefRecordType.toLatin1());
    ndefRecord.setPayload(m_ndefRecordPayload.toLatin1());

    QNdefMessage ndefMessage;
    ndefMessage.append(ndefRecord);

    QList<QNdefMessage> ndefMessages;
    ndefMessages.append(ndefMessage);

    target->writeNdefMessages(ndefMessages);
}

void NearFieldItem::setError(ErrorCategory errorCategory, int error)
{
    if ((m_errorCategory != errorCategory) || (m_error != error)) {
        m_errorCategory = errorCategory;
        m_error = error;

        emit errorChanged(errorCategory, error);
    }
}
